package com.duan.common.utils.redis;

import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisCluster;
import redis.clients.jedis.JedisCommands;

import java.util.Collections;
import java.util.concurrent.TimeUnit;

public class RedisLock {

    String scripts;


    private static final String SET_IF_NOT_EXISTS = "NX";
    private static final String SET_WITH_EXPIRE_TIME = "PX";
    private static final Integer EXPIRE_TIME = 1000; //毫秒
    private static final String LOCK_MSG = "OK";
    private static final Long UNLOCK_MSG = 1L;

    private JedisCommands jedis;
    private String LOCK_PREFIX;
    private Integer Default_SLEEP_TIME;

    private RedisLock(Builder builder){
        this.jedis = builder.jedis;
        this.LOCK_PREFIX = builder.LOCK_PREFIX;
        this.Default_SLEEP_TIME = builder.Default_SLEEP_TIME;
        scripts = ScriptUtil.buildScript("lock.lua");
    }


    /**
     * 非阻塞锁 等待10秒
     * @param key
     * @param request
     * @return
     */
    public boolean tryLock(String key, String request) {
        String lockResult = jedis.set(LOCK_PREFIX + key, request, SET_IF_NOT_EXISTS, SET_WITH_EXPIRE_TIME, 10 * EXPIRE_TIME);
        if (LOCK_MSG.equals(lockResult)) {
            return true;
        }
        return false;
    }

    /**
     * 非阻塞锁
     * @param key
     * @param request
     * @param expireTime 毫秒
     * @return
     */
    public boolean tryLock(String key, String request, Integer expireTime) {
        String lockResult = jedis.set(LOCK_PREFIX + key, request, SET_IF_NOT_EXISTS, SET_WITH_EXPIRE_TIME, expireTime);
        if (LOCK_MSG.equals(lockResult)) {
            return true;
        }
        return false;
    }


    /**
     * 阻塞锁
     * @param key
     * @param request
     * @return
     */
    public boolean lock(String key, String request) throws InterruptedException {
        for (;;) {
            String lockResult = jedis.set(LOCK_PREFIX + key, request, SET_IF_NOT_EXISTS, SET_WITH_EXPIRE_TIME, 10 * EXPIRE_TIME);
            if (LOCK_MSG.equals(lockResult)) {
                return true;
            }
            TimeUnit.MILLISECONDS.sleep(Default_SLEEP_TIME);
        }
    }


    /**
     * 解锁
     * @param key
     * @param request
     * @return
     */
    public boolean unLock(String key, String request) {
        Object result ;
        if (jedis instanceof Jedis) {
            result = ((Jedis)jedis).eval(scripts, Collections.singletonList(LOCK_PREFIX + key), Collections.singletonList(request));
        } else if (jedis instanceof JedisCluster) {
            result = ((JedisCluster) jedis).eval(scripts, Collections.singletonList(key), Collections.singletonList(request));
        } else {
            throw new RuntimeException("没有改jedis对象");
        }
        System.out.println(result.getClass().getName());
        if (UNLOCK_MSG.equals(result)) {
            return true;
        }
        return false;
    }


    public static class Builder<T extends JedisCommands> {
        private T jedis;
        private String LOCK_PREFIX = "lock_";
        private Integer Default_SLEEP_TIME = 100;

        public Builder (T jedis) {
            this.jedis = jedis;
        }

        public Builder lockPrefix(String lockPrefix) {
            this.LOCK_PREFIX = lockPrefix;
            return this;
        }

        public Builder sleeptime (Integer sleepTime) {
            this.Default_SLEEP_TIME = sleepTime;
            return this;
        }

        public RedisLock build() {
            return new RedisLock(this);
        }
    }

}
